/**
  ******************************************************************************
  * @file    ymodem.c
  * @author  ashuai0110
  * @version V2.3
  * @date    2024-07-01
  * @brief   ymodem协议
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  * Change Logs:
  * Date          Author        Notes
  * 2024-01-17    ashuai0110    完成基本内容
  * 2024-01-21    ashuai0110    增加收发轮询处理返回状态
  * 2024-01-22    ashuai0110    增加发送缓存读出完毕关闭发送中断的处理
  * 2024-03-04    ashuai0110    增加接收到脏数据时重发当前包的处理,增加部分接口
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "ymodem.h"

/**
 * @addtogroup bootloader
 * @{
 */

/**
 * @defgroup ymodem ymodem
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/**
 * @defgroup ymodem_local_macros ymodem local macros
 * @{
 */

/**
 * @defgroup ymodem_check_parameters_validity ymodem check parameters validity
 * @{
 */
/* ymodem接收标志有效性检查 */
#define IS_VALID_RX_FLAG(x)                                                   \
(       ((x) == YMODEM_RX_FLAG_NAK)    ||                                     \
        ((x) == YMODEM_RX_FLAG_CRC))

/**
 * @}
 */

/**
 * @}
 */

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/
/**
 * @defgroup ymodem_local_functions ymodem local functions
 * @{
 */

/**
  * @brief  CRC16-YMODEM校验
  *
  * @param  data : 校验数据
  *
  * @param  len  : 校验数据长度
  *
  * @retval 校验结果
  */
static uint16_t ymodem_check_crc16(uint8_t *data, uint16_t len)
{
    uint8_t cnt;
    uint16_t crc = 0;

    while(len--)
    {
        crc ^= (uint16_t)(*data) << 8;
        data++;
        for(cnt = 0; cnt < 8; ++cnt)
        {
            if(crc & 0x8000)
            {
                crc = (crc << 1) ^ 0x1021;
            }
            else
            {
                crc <<= 1;
            }
        }
    }

    return crc;
}

/**
  * @brief  累加和校验
  *
  * @param  data : 校验数据
  *
  * @param  len  : 校验数据长度
  *
  * @retval 累加和
  */
static uint8_t ymodem_check_sum(uint8_t *data, uint16_t len)
{
    uint8_t sum = 0;
    uint16_t cnt = 0;

    for(; cnt < len; cnt++)
    {
        sum += data[cnt];
    }

    return sum;
}

/**
  * @brief  ymodem初始状态
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @retval None
  */
inline static void ymodem_init_state(ymodem_t *_ymodem)
{
    _ymodem->dataLen = 0;
    _ymodem->readLen = 0;
    _ymodem->dataLenTmp = 0;
    _ymodem->rspTimeoutCnt = 0;
    _ymodem->resendTimesCnt = 0;
    _ymodem->packetNum = 0;
    _ymodem->state = YMODEM_STATE_IDLE;
    _ymodem->fileFlag = 0;
    _ymodem->eotFlag = 0;
    _ymodem->recFlag = 0;
}

/**
 * @}
 */

/**
 * @defgroup ymodem_global_functions ymodem global functions
 * @{
 */

/**
  * @brief  ymodem初始化
  *
  * @param  _ymodem     : ymodem管理实例
  *
  * @param  pBuf        : 数据缓存区
  *
  * @param  bufLen      : 数据缓存区长度byte(根据ymodem.h:50行ymodem协议格式定义长度)
  *
  * @param  txEnFn      : 串口发送前准备函数
  *
  * @param  getPacketFn : 获取数据包回调函数(不用可填NULL)
  *
  * @param  setPacketFn : 设置数据包回调函数(不用可填NULL)
  *
  * @param  rspTimeout  : 回复超时时间ms
  *
  * @param  resendTimes : 超时重发次数
  *
  * @retval None
  */
void ymodem_init(ymodem_t *_ymodem, void *dataBuf, uint16_t bufLen, ymodem_tx_en_t txEnFn, \
    ymodem_user_packet_cb_t getPacketFn, ymodem_user_packet_cb_t setPacketFn, uint16_t rspTimeout, uint8_t resendTimes)
{
    ASSERT_PARAM(IS_VALID_POINT(_ymodem));
    ASSERT_PARAM(IS_VALID_POINT(dataBuf));
    ASSERT_PARAM(IS_VALID_POINT(txEnFn));
    
    /* 初始化各参数 */
    memset(_ymodem, 0, sizeof(ymodem_t));
    _ymodem->dataBuf = (uint8_t *)dataBuf;
    _ymodem->dataBufLen = bufLen;
    _ymodem->txEnFn = txEnFn;
    _ymodem->getPacketFn = getPacketFn;
    _ymodem->setPacketFn = setPacketFn;
    _ymodem->rspTimeout = rspTimeout;
    _ymodem->resendTimes = resendTimes;
}

/**
  * @brief  ymodem接收轮询处理
  *
  * @param  _ymodem     : ymodem管理实例
  *
  * @retval 接收轮询状态
  *    @arg RET_OK      : 传输完成
  *    @arg RET_ERR     : 传输错误
  *    @arg RET_ING     : 进行中(默认处于此状态)
  *    @arg RET_TIMEOUT : 回复超时
  */
uint8_t ymodem_rx_poll(ymodem_t *_ymodem)
{
    uint8_t ret = RET_ING;
    uint32_t tickTmp;

    ASSERT_PARAM(IS_VALID_POINT(_ymodem));
    
    switch(_ymodem->state)
    {
        case YMODEM_STATE_IDLE: break;
        case YMODEM_STATE_WAIT:
            tickTmp = _ymodem->rspTimeoutCnt;
            /* 应答超时判断 */
            if(tickTmp && (module_tick_get() - tickTmp) > _ymodem->rspTimeout)
            {
                _ymodem->rspTimeoutCnt = 0;
                /* 接受标志置位说明接收到了脏数据 */
                if(_ymodem->recFlag)
                {
                    _ymodem->dataLenTmp = _ymodem->dataLen;
                    _ymodem->state = YMODEM_STATE_CHECK_DATA; /* 切换状态 */
                    break;
                }
                /* 若回复超时次数达到预设值 则直接中断接收回到IDLE状态 */
                if(_ymodem->resendTimesCnt++ >= _ymodem->resendTimes)
                {
                    ymodem_init_state(_ymodem);
                    ret = RET_TIMEOUT; /* 回复超时 */
                    break;
                }
                /* 第二次收到EOT应答超时后重新启动接收 */
                if(2 == _ymodem->eotFlag)
                {
                    ymodem_init_state(_ymodem);
                    ymodem_rx_start(_ymodem, _ymodem->checkNum == 1 ? YMODEM_NAK : YMODEM_CRC16);
                    break;
                }
                /* 重发当前包 */
                _ymodem->dataLen = _ymodem->dataLenTmp;
                _ymodem->txEnFn(1);
            }
            /* 每次收到文件信息包后要重新回复启动内容 */
            if(_ymodem->fileFlag && _ymodem->packetNum)
            {
                _ymodem->fileFlag = 0;
                /* 发送回复内容 */
                _ymodem->dataBuf[YMODEM_START_INDEX] = _ymodem->checkNum == 1 ? YMODEM_NAK : YMODEM_CRC16;
                _ymodem->dataLen = 1;
                _ymodem->dataLenTmp = _ymodem->dataLen;
                _ymodem->txEnFn(1);
            }
            break;
        case YMODEM_STATE_CHECK_HEAD:
            /* 根据包头和数据长度进行初步检查 */
            /* SOH为包头 携带128byte数据 */
            if(YMODEM_SOH == _ymodem->dataBuf[YMODEM_START_INDEX] && (YMODEM_HEADER_SIZE + YMODEM_DATA_SIZE + _ymodem->checkNum) <= _ymodem->dataLen)
            {
                /* 结束包 数据是0 */
                if(0 == _ymodem->packetNum && 0 == _ymodem->dataBuf[YMODEM_DATA_INDEX])
                {
                    /* 官方定义结束包的数据全是0 但有些工具并不是 此处为兼容大部分只判断一部分数据 */
                    if(0 == ymodem_check_sum(&_ymodem->dataBuf[30], 100))
                    {
                        ymodem_init_state(_ymodem);
                        /* 回复ACK */
                        _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_ACK;
                        _ymodem->dataLen = 1;
                        _ymodem->dataLenTmp = _ymodem->dataLen;
                        _ymodem->txEnFn(1);
                        ret = RET_OK; /* 传输完成 */
                        break;
                    }
                }
                /* 数据包 */
                _ymodem->state = YMODEM_STATE_CHECK_DATA; /* 切换状态 */
                _ymodem->dataLenTmp = YMODEM_DATA_SIZE;
            } /* STX为包头 携带1024byte数据 */
            else if(YMODEM_STX == _ymodem->dataBuf[YMODEM_START_INDEX] && (YMODEM_HEADER_SIZE + YMODEM_DATA_1K_SIZE + _ymodem->checkNum) <= _ymodem->dataLen)
            {
                _ymodem->state = YMODEM_STATE_CHECK_DATA; /* 切换状态 */
                _ymodem->dataLenTmp = YMODEM_DATA_1K_SIZE;
            } /* EOT为包头 表示传输结束 */
            else if(YMODEM_EOT == _ymodem->dataBuf[YMODEM_START_INDEX])
            {
                /* 第二次收到EOT则回复ACK */
                if(_ymodem->eotFlag)
                {
                    _ymodem->eotFlag = 2;
                    _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_ACK; /* 回复ACK */
                } /* 第一次收到EOT回复NAK */
                else
                {
                    _ymodem->eotFlag = 1;
                    _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_NAK; /* 回复NAK */
                }
                _ymodem->state = YMODEM_STATE_WAIT; /* 切换状态 */
                /* 发送回复内容 */
                _ymodem->dataLen = 1;
                _ymodem->dataLenTmp = _ymodem->dataLen;
                _ymodem->txEnFn(1);
                break;
            } /* CAN为包头 表示传输取消 */
            else if(YMODEM_CAN == _ymodem->dataBuf[YMODEM_START_INDEX])
            {
                ymodem_init_state(_ymodem);
                ret = RET_ERR; /* 传输错误 */
                break;
            } /* 其它则回到等待状态 */
            else
            {
                _ymodem->recFlag = 1; /* 置位接收标志 */
                _ymodem->state = YMODEM_STATE_WAIT; /* 切换状态 */
                _ymodem->rspTimeoutCnt = module_tick_get(); /* 开启超时计数 */
                break;
            }
        case YMODEM_STATE_CHECK_DATA:
            /* 包序号和包序号补码满足条件 */
            if(_ymodem->packetNum == _ymodem->dataBuf[YMODEM_NUMBER_INDEX] && 0xFF == (_ymodem->dataBuf[YMODEM_NUMBER_INDEX] + _ymodem->dataBuf[YMODEM_CNUMBER_INDEX]))
            {
                /* 1字节累加和校验 */
                if(1 == _ymodem->checkNum)
                {
                    /* 校验成功 */
                    if(_ymodem->dataBuf[_ymodem->dataLenTmp + YMODEM_HEADER_SIZE] == ymodem_check_sum(&_ymodem->dataBuf[YMODEM_DATA_INDEX], _ymodem->dataLenTmp))
                    {
                        /* 用户get回调函数 */
                        _ymodem->getPacketFn(_ymodem->packetNum++, &_ymodem->dataBuf[YMODEM_DATA_INDEX], &_ymodem->dataLenTmp);
                        _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_ACK; /* 回复ACK */
                    } /* 校验失败 */
                    else
                    {
                        _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_NAK; /* 回复NAK */
                    }
                } /* 2字节CRC16校验 */
                else if(2 == _ymodem->checkNum)
                {
                    /* 校验成功 */
                    if(0 == ymodem_check_crc16(&_ymodem->dataBuf[YMODEM_DATA_INDEX], _ymodem->dataLenTmp + _ymodem->checkNum))
                    {
                        _ymodem->getPacketFn(_ymodem->packetNum++, &_ymodem->dataBuf[YMODEM_DATA_INDEX], &_ymodem->dataLenTmp); /* 用户回调函数 */
                        _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_ACK; /* 回复ACK */
                    } /* 校验失败 */
                    else
                    {
                        _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_NAK; /* 回复NAK */
                    }
                }
            } /* 否则回复NAK */
            else
            {
                _ymodem->dataBuf[YMODEM_START_INDEX] = YMODEM_NAK;
            }
            /* 恢复默认参数 */
            _ymodem->recFlag = 0;
            _ymodem->resendTimesCnt = 0;
            _ymodem->state = YMODEM_STATE_WAIT; /* 切换状态 */
            /* 发送回复内容 */
            _ymodem->dataLen = 1;
            _ymodem->dataLenTmp = _ymodem->dataLen;
            _ymodem->txEnFn(1);
            break;
        default: 
            ymodem_init_state(_ymodem);
            ret = RET_ERR; /* 传输错误 */
            break;
    }
    
    return ret;
}

/**
  * @brief  ymodem接收启动
  *
  * @param  _ymodem            : ymodem管理实例
  *
  * @param  startFlag          : 接收启动标志 @ref ymodem_rx_flag
  *   @arg  YMODEM_RX_FLAG_NAK : 接收方发送YMODEM_NAK启动传输,发送方使用校验和
  *   @arg  YMODEM_RX_FLAG_CRC : 接收方发送'C'启动传输,发送方使用CRC校验
  *
  * @retval None
  */
void ymodem_rx_start(ymodem_t *_ymodem, uint8_t startFlag)
{
    ASSERT_PARAM(IS_VALID_POINT(_ymodem));
    ASSERT_PARAM(IS_VALID_RX_FLAG(startFlag));
    
    if(YMODEM_STATE_IDLE == _ymodem->state)
    {
        _ymodem->checkNum = (startFlag == YMODEM_RX_FLAG_NAK) ? 1 : 2; /* 记录校验方式 */
        _ymodem->fileFlag = 1; /* 置位文件标志 */
        _ymodem->state = YMODEM_STATE_WAIT; /* 切换状态 */
        /* 发送启动内容 */
        _ymodem->dataBuf[YMODEM_START_INDEX] = startFlag;
        _ymodem->dataLen = 1;
        _ymodem->dataLenTmp = _ymodem->dataLen;
        _ymodem->txEnFn(1);
    }
}

/**
  * @brief  ymodem发送轮询处理
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @retval 发送轮询状态
  *    @arg RET_OK      : 传输完成
  *    @arg RET_ERR     : 传输错误
  *    @arg RET_ING     : 进行中(默认处于此状态)
  *    @arg RET_TIMEOUT : 回复超时
  */
uint8_t ymodem_tx_poll(ymodem_t *_ymodem)
{
    uint8_t ret = RET_ING;
    uint16_t checkVal;
    uint32_t tickTmp;
    
    ASSERT_PARAM(IS_VALID_POINT(_ymodem));
    
    switch(_ymodem->state)
    {
        case YMODEM_STATE_IDLE:
            /* 逐字节查询启动命令 */
            if(_ymodem->readLen < _ymodem->dataLen)
            {
                /* 收到NAK则使用累加和校验 */
                if(YMODEM_NAK == _ymodem->dataBuf[_ymodem->readLen])
                {
                    _ymodem->checkNum = 1;
                } /* 收到'C'则使用CRC16校验 */
                else if(YMODEM_CRC16 == _ymodem->dataBuf[_ymodem->readLen])
                {
                    _ymodem->checkNum = 2;
                } /* 否则认为是无效数据 */
                else
                {
                    _ymodem->readLen++;
                    break;
                }
                /* 首次收到启动命令则回复文件信息包 */
                if(0 == _ymodem->packetNum)
                {
                    _ymodem->fileFlag = 1;
                }
                _ymodem->state = YMODEM_STATE_CHECK_DATA; /* 切换状态 */
            }
            break;
        case YMODEM_STATE_WAIT:
            tickTmp = _ymodem->rspTimeoutCnt;
            /* 应答超时判断 */
            if(tickTmp && (module_tick_get() - tickTmp) > _ymodem->rspTimeout)
            {
                _ymodem->rspTimeoutCnt = 0;
                /* 若回复超时次数达到预设值 则直接中断接收回到IDLE状态 */
                if(_ymodem->resendTimesCnt++ >= _ymodem->resendTimes)
                {
                    ymodem_init_state(_ymodem);
                    ret = RET_TIMEOUT; /* 回复超时 */
                    break;
                }
                /* 重发当前包 */
                _ymodem->dataLen = _ymodem->dataLenTmp;
                _ymodem->txEnFn(1);
            }
            break;
        case YMODEM_STATE_CHECK_HEAD:
            /* 收到ACK则继续发下一包 */
            if(YMODEM_ACK == _ymodem->dataBuf[0])
            {
                /* 文件标志置位表明此前发出的是文件信息包 收到ACK则返回IDLE状态 */
                if(_ymodem->fileFlag)
                {
                    _ymodem->fileFlag = 0;
                    _ymodem->dataLen = 0;
                    _ymodem->state = YMODEM_STATE_IDLE; /* 切换状态 */
                    break;
                }
                /* 传输结束标志置位表明此前发出的是EOT 收到ACK表明传输结束则回到初始状态 */
                if(_ymodem->eotFlag)
                {
                    ymodem_init_state(_ymodem);
                    ret = RET_OK; /* 传输完成 */
                    break;
                }
                /* 其它是正常数据 切换到数据检验状态 */
                _ymodem->state = YMODEM_STATE_CHECK_DATA; /* 切换状态 */
            } /* 收到NAK则重发当前包 */
            else if(YMODEM_NAK == _ymodem->dataBuf[0])
            {
                _ymodem->packetNum--; /* 包序号-1 */
                _ymodem->state = YMODEM_STATE_CHECK_DATA; /* 切换状态 */
            } /* 收到CAN则取消传输 */
            else if(YMODEM_CAN == _ymodem->dataBuf[0])
            {
                ymodem_init_state(_ymodem);
                ret = RET_ERR; /* 传输错误 */
                break;
            } /* 否则认为是无效数据 */
            else
            {
                _ymodem->dataLen = 0;
                _ymodem->state = YMODEM_STATE_WAIT; /* 切换状态 */
                _ymodem->rspTimeoutCnt = module_tick_get(); /* 开启超时计数 */
                break;
            }
        case YMODEM_STATE_CHECK_DATA:
            _ymodem->readLen = 0;
            _ymodem->resendTimesCnt = 0;
            _ymodem->state = YMODEM_STATE_WAIT; /* 切换状态 */
            /* 用户set回调函数 */
            _ymodem->setPacketFn(_ymodem->packetNum, &_ymodem->dataBuf[3], &_ymodem->dataLen);
            /* 超过128但不超过1024的按1024byte */
            if(YMODEM_DATA_SIZE < _ymodem->dataLen && YMODEM_DATA_1K_SIZE >= _ymodem->dataLen)
            {
                _ymodem->dataBuf[0] = YMODEM_STX; /* 包头STX携带1024byte数据 */
                memset(&_ymodem->dataBuf[3 + _ymodem->dataLen], YMODEM_CTRLZ, YMODEM_DATA_1K_SIZE - _ymodem->dataLen); /* 填充字节 */
                _ymodem->dataLen = YMODEM_DATA_1K_SIZE;
            } /* 超过0但不超过128的按128byte */
            else if(0 < _ymodem->dataLen && YMODEM_DATA_SIZE >= _ymodem->dataLen)
            {
                _ymodem->dataBuf[0] = YMODEM_SOH; /* 包头STX携带128byte数据 */
                memset(&_ymodem->dataBuf[3 + _ymodem->dataLen], YMODEM_CTRLZ, YMODEM_DATA_SIZE - _ymodem->dataLen); /* 填充字节 */
                _ymodem->dataLen = YMODEM_DATA_SIZE;
            } /* 其它认为是传输结束 */
            else
            {
                _ymodem->eotFlag = 1;
                /* 回复EOT传输结束 */
                _ymodem->dataBuf[0] = YMODEM_EOT;
                _ymodem->dataLen = 1;
                _ymodem->dataLenTmp = _ymodem->dataLen;
                _ymodem->txEnFn(1);
                break;
            }
            /* 1字节累加和校验 */
            if(1 == _ymodem->checkNum)
            {
                checkVal = ymodem_check_sum(&_ymodem->dataBuf[3], _ymodem->dataLen);
                _ymodem->dataBuf[_ymodem->dataLen + 3] = checkVal;
            } /* 2字节CRC16校验 */
            else
            {
                checkVal = ymodem_check_crc16(&_ymodem->dataBuf[3], _ymodem->dataLen);
                _ymodem->dataBuf[_ymodem->dataLen + 3] = checkVal >> 8;
                _ymodem->dataBuf[_ymodem->dataLen + 4] = checkVal & 0xFF;
            }
            /* 设置包序号和其反码 */
            _ymodem->dataBuf[1] = _ymodem->packetNum;
            _ymodem->dataBuf[2] = 0xFF - _ymodem->packetNum;
            _ymodem->packetNum++;
            /* 发送包内容 */
            _ymodem->dataLen = _ymodem->dataLen + 3 + _ymodem->checkNum;
            _ymodem->dataLenTmp = _ymodem->dataLen;
            _ymodem->txEnFn(1);
            break;
        default: 
            ymodem_init_state(_ymodem);
            ret = RET_ERR; /* 传输错误 */
            break;
    }
    
    return ret;
}

/**
  * @brief  写入数据到接收缓冲区
  *
  * @note   一般加入到(串口)接收中断
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @param  pBuf    : 数据缓存区
  *
  * @param  len     : 写入长度byte
  *
  * @retval uint16_t
  *    @arg !0      : 成功写入的数据数量
  *    @arg 0       : 失败(写入已满)
  */
uint16_t ymodem_write_rx_buf(ymodem_t *_ymodem, void *pBuf, uint16_t len)
{
    uint16_t unusedLen;
    
    ASSERT_PARAM(IS_VALID_POINT(_ymodem));
    ASSERT_PARAM(IS_VALID_POINT(_ymodem->dataBuf));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    
    /* 在指定状态可以接收数据 */
    if(YMODEM_STATE_CHECK_HEAD >= _ymodem->state)
    {
        /* 计算可写数量 */
        unusedLen = _ymodem->dataBufLen - _ymodem->dataLen;
        len = (len > unusedLen) ? unusedLen : len;
        
        if(0 < len)
        {
            /* 接续copy数据并记录数量 */
            memcpy((void *)&_ymodem->dataBuf[_ymodem->dataLen], (void *)pBuf, len);
            _ymodem->dataLen += len;
            _ymodem->rspTimeoutCnt = 0; /* 关闭回复超时计数 */
            if(YMODEM_STATE_WAIT == _ymodem->state)
            {
                _ymodem->state = YMODEM_STATE_CHECK_HEAD; /* 切换状态 */
            }

            return len;
        }
    }
    
    return 0;
}

/**
  * @brief  读出数据从发送缓冲区
  *
  * @note   一般加入到(串口)发送中断
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @param  pBuf    : 数据存储区
  *
  * @param  len     : 读出长度byte
  *
  * @retval uint16_t
  *    @arg !0      : 成功读出的数据数量
  *    @arg 0       : 失败(无数据可读)
  */
uint16_t ymodem_read_tx_buf(ymodem_t *_ymodem, void *pBuf, uint16_t len)
{
    uint16_t unreadLen;
    
    ASSERT_PARAM(IS_VALID_POINT(_ymodem));
    ASSERT_PARAM(IS_VALID_POINT(_ymodem->dataBuf));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    
    /* 计算可读数量 */
    unreadLen = _ymodem->dataLen - _ymodem->readLen;
    len = (len > unreadLen) ? unreadLen : len;
    
    if(0 < len)
    {
        /* copy数据至外部存储区并记录数量 */
        memcpy((void *)pBuf, (void *)&_ymodem->dataBuf[_ymodem->readLen], len);
        _ymodem->readLen += len;
        /* 数据被全部copy出去 */
        if(_ymodem->readLen == _ymodem->dataLen)
        {
            /* 清零相关计数 */
            _ymodem->dataLen = 0;
            _ymodem->readLen = 0;
            _ymodem->rspTimeoutCnt = module_tick_get(); /*开启回复超时计数*/
        }
    }
    else
    {
        _ymodem->txEnFn(0); /* 关闭发送中断 */
    }
    
    return len;
}

/**
  * @brief  将文件信息写入包中
  *
  * @param  _info : 文件信息结构体
  *
  * @param  data  : ymodem数据
  *
  * @retval None
  */
void ymodem_set_file_info(ymodem_t *_ymodem, file_info_t *_info)
{
    uint8_t lenTmp;
    /* 清零数据缓冲区 */
    memset((void *)_ymodem->dataBuf, 0, YMODEM_HEADER_SIZE + YMODEM_DATA_SIZE);
    /* 文件大小为0则没有文件需要传输 */
    if(0 != _info->fileSize)
    {
        lenTmp = strlen(_info->fileName); /* 获取文件名称长度 */
        /* 写入文件名称 */
        memcpy((void *)&_ymodem->dataBuf[YMODEM_DATA_INDEX], (void *)_info->fileName, lenTmp);
        /* 写入文件大小 */
        sprintf((char *)&_ymodem->dataBuf[YMODEM_DATA_INDEX + lenTmp + 1], "%d", _info->fileSize);
        _ymodem->dataLen = YMODEM_DATA_SIZE;
    }
}

/**
  * @brief  从包中获取文件信息
  *
  * @param  _info : 文件信息结构体指针
  *
  * @param  data  : ymodem数据
  *
  * @retval None
  */
void ymodem_get_file_info(ymodem_t *_ymodem, file_info_t *_info)
{
    uint8_t lenTmp;
    /* 获取文件名称长度 不能超出可存放长度 */
    lenTmp = strlen((char *)&_ymodem->dataBuf[YMODEM_DATA_INDEX]);
    lenTmp = lenTmp > YMODEM_FILE_NAME_LEN ? YMODEM_FILE_NAME_LEN : lenTmp;
    /* 读出文件名称 */
    memcpy((void *)_info->fileName, (void *)&_ymodem->dataBuf[YMODEM_DATA_INDEX], lenTmp);
    _info->fileName[lenTmp] = '\0';
    /* 读出文件长度 */
    _info->fileSize = atoi((char *)&_ymodem->dataBuf[YMODEM_DATA_INDEX + lenTmp + 1]);
}

/**
  * @brief  ymodem获取文件标志
  *
  * @note   用户通过此接口区分文件信息包或正常数据包
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @retval 返回文件标志
  *    @arg 0 : 正常数据包
  *    @arg 1 : 文件信息包
  */
uint8_t ymodem_get_file_flag(ymodem_t *_ymodem)
{
    return _ymodem->fileFlag;
}

/**
  * @brief  ymodem设置回复超时时间ms
  *
  * @param  _ymodem    : ymodem管理实例
  *
  * @param  rspTimeout : 回复超时时间ms
  *
  * @retval None
  */
void ymodem_set_rsp_timeout(ymodem_t *_ymodem, uint16_t rspTimeout)
{
    _ymodem->rspTimeout = rspTimeout; 
}

/**
  * @brief  ymodem获取回复超时时间ms
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @retval 返回回复超时时间ms
  */
uint16_t ymodem_get_rsp_timeout(ymodem_t *_ymodem)
{
    return _ymodem->rspTimeout; 
}

/**
  * @brief  ymodem设置重发次数
  *
  * @param  _ymodem     : ymodem管理实例
  *
  * @param  resendTimes : 重发次数
  *
  * @retval None
  */
void ymodem_set_resend_times(ymodem_t *_ymodem, uint8_t resendTimes)
{
    _ymodem->resendTimes = resendTimes; 
}

/**
  * @brief  ymodem获取重发次数
  *
  * @param  _ymodem : ymodem管理实例
  *
  * @retval 返回重发次数
  */
uint8_t ymodem_get_resend_times(ymodem_t *_ymodem)
{
    return _ymodem->resendTimes; 
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
